package com.jing.yao.modules.resource.service.impl;

import com.jing.yao.bean.KeyValue;
import com.jing.yao.bean.TreeNode;
import com.jing.yao.code.UserCode;
import com.jing.yao.component.constants.BaseOperationEnum;
import com.jing.yao.component.mapper.YaoBaseMapper;
import com.jing.yao.constant.Enabled;
import com.jing.yao.exception.BusinessException;
import com.jing.yao.exception.BusinessExceptionBuilder;
import com.jing.yao.modules.resource.dao.ResourceMapper;
import com.jing.yao.modules.resource.dao.RoleResourceMapper;
import com.jing.yao.modules.resource.dao.entity.ResourceEntity;
import com.jing.yao.modules.resource.dao.entity.RoleResourceEntity;
import com.jing.yao.modules.resource.service.ResourceService;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 菜单管理管理Service
 *
 * @author lirui
 * @since 2020-06-24
 */
@Service
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;

    @Autowired
    private RoleResourceMapper roleResourceMapper;

    @Override
    public YaoBaseMapper<ResourceEntity> getMapper() {
        return resourceMapper;
    }

    /**
     * 获取角色对应的权限
     *
     * @param roles
     * @return
     */
    @Override
    public Set<String> getUserAuthorities(List<String> roles) {
        if (CollectionUtils.isEmpty(roles)) {
            return new HashSet<>();
        }
        LambdaQueryWrapper<RoleResourceEntity> queryWrapper = Wrappers.<RoleResourceEntity>lambdaQuery()
                .eq(RoleResourceEntity::getIsMenu, Enabled.NO.getValue())
                .in(RoleResourceEntity::getRoleCode, roles);

        return roleResourceMapper.selectList(queryWrapper)
                .stream()
                .map(RoleResourceEntity::getResCode)
                .collect(Collectors.toSet());
    }


    /**
     * 获取角色对应的菜单
     *
     * @param roles
     * @return
     */
    @Override
    public List<ResourceEntity> getUserMenus(List<String> roles) {

        if (CollectionUtils.isEmpty(roles)) {
            return new ArrayList<>();
        }

        LambdaQueryWrapper<RoleResourceEntity> queryWrapper = Wrappers.<RoleResourceEntity>lambdaQuery()
                .eq(RoleResourceEntity::getIsMenu, Enabled.YES.getValue())
                .in(RoleResourceEntity::getRoleCode, roles);
        //菜单编码
        Set<String> menuCodes = roleResourceMapper.selectList(queryWrapper)
                .stream()
                .map(RoleResourceEntity::getResCode)
                .collect(Collectors.toSet());

        //获取当前用户所有菜单
        LambdaQueryWrapper<ResourceEntity> resourceQueryWrapper = Wrappers.<ResourceEntity>lambdaQuery()
                .eq(ResourceEntity::getEnabled, Enabled.YES.getValue())
                .in(ResourceEntity::getResCode, menuCodes)
                .orderByAsc(ResourceEntity::getSortNo);
        List<ResourceEntity> allResources = resourceMapper.selectList(resourceQueryWrapper);
        //一级菜单（没有父菜单）
        List<ResourceEntity> rootResources = allResources.stream().filter(resource -> StringUtils.isBlank(resource.getParentCode())).collect(Collectors.toList());

        //遍及一级菜单，找出它的下级菜单
        rootResources.stream().forEach(resource -> {
            resource.setChildren(getSubResources(resource.getResCode(), allResources));
        });

        return rootResources;
    }

    /**
     * 递归
     *
     * @param resCode
     * @param resources
     * @return
     */
    public List<ResourceEntity> getSubResources(String resCode, List<ResourceEntity> resources) {
        List<ResourceEntity> subResources = new ArrayList<>();
        resources.stream().forEach(resource -> {
            //当前code的下级菜单
            if (StringUtils.equals(resCode, resource.getParentCode())) {
                subResources.add(resource);
            }
        });

        if (subResources.isEmpty()) {
            return new ArrayList<>();
        }

        //编辑子菜单
        subResources.stream().forEach(resource -> {
            resource.setChildren(getSubResources(resource.getResCode(), resources));
        });

        return subResources;
    }

    @Override
    public List<KeyValue> listParents() {
        Wrapper<ResourceEntity> wrapper = Wrappers.<ResourceEntity>lambdaQuery()
                .eq(ResourceEntity::getParentFlag, Enabled.YES.getValue())
                .eq(ResourceEntity::getEnabled, Enabled.YES.getValue());

        return getCollect(wrapper);
    }

    @Override
    public List<KeyValue> listMenus() {
        Wrapper<ResourceEntity> wrapper = Wrappers.<ResourceEntity>lambdaQuery()
                .eq(ResourceEntity::getEnabled, Enabled.YES.getValue());

        return getCollect(wrapper);
    }

    private List<KeyValue> getCollect(Wrapper<ResourceEntity> wrapper) {
        return list(wrapper)
                .stream().map(resourceEntity -> {
                    KeyValue keyValue = new KeyValue();
                    keyValue.setId(resourceEntity.getResCode());
                    keyValue.setText(resourceEntity.getResName());
                    return keyValue;
                }).collect(Collectors.toList());
    }


    private List<ResourceEntity> listParentEntities() {
        Wrapper<ResourceEntity> wrapper = Wrappers.<ResourceEntity>lambdaQuery()
                .eq(ResourceEntity::getParentFlag, Enabled.YES.getValue())
                .eq(ResourceEntity::getEnabled, Enabled.YES.getValue());

        return list(wrapper);
    }

    @Override
    public List<TreeNode> getTree() {

        //获取所有菜单和权限
        LambdaQueryWrapper<ResourceEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ResourceEntity::getEnabled, Enabled.YES.getValue());
        List<ResourceEntity> allResources = resourceMapper.selectList(wrapper);
        //一级菜单（没有父菜单）
        List<TreeNode> rootNodes = allResources.stream()
                .filter(resource -> StringUtils.isBlank(resource.getParentCode()))
                .map(resourceEntity -> {
                    TreeNode node = new TreeNode();
                    node.setId(resourceEntity.getResCode());
                    node.setLabel(resourceEntity.getResName());
                    return node;
                })
                .collect(Collectors.toList());

        //遍及一级菜单，找出它的下级菜单
        rootNodes.stream().forEach(node -> {
            node.setChildren(getSubTreeNode(node.getId(), allResources));
        });

        return rootNodes;
    }

    /**
     * 递归
     *
     * @param resCode
     * @param resources
     * @return
     */
    public List<TreeNode> getSubTreeNode(String resCode, List<ResourceEntity> resources) {
        List<TreeNode> subNodes = new ArrayList<>();
        resources.stream().forEach(resource -> {
            //当前code的下级菜单
            if (StringUtils.equals(resCode, resource.getParentCode())) {
                TreeNode node = new TreeNode();
                node.setId(resource.getResCode());
                node.setLabel(resource.getResName());
                subNodes.add(node);
            }
        });
        if (subNodes.isEmpty()) {
            return new ArrayList<>();
        }

        //编辑子菜单
        subNodes.stream().forEach(resource -> {
            resource.setChildren(getSubTreeNode(resource.getId(), resources));
        });

        return subNodes;
    }

    @Override
    public List<String> getRoleTree(String roleCode) {
        LambdaQueryWrapper<RoleResourceEntity> lambdaQueryWrapper = Wrappers.<RoleResourceEntity>lambdaQuery().eq(RoleResourceEntity::getRoleCode, roleCode);
        return roleResourceMapper.selectList(lambdaQueryWrapper).stream().map(RoleResourceEntity::getResCode).collect(Collectors.toList());
    }

    @Override
    public void processBeforeOperation(ResourceEntity entity, BaseOperationEnum operationEnum) throws BusinessException {
        switch (operationEnum) {
            case INSERT:
                if (StringUtils.isBlank(entity.getParentCode())) {
                    entity.setParentFlag(Enabled.YES.getValue());
                }
                String resCode = entity.getResCode();
                ResourceEntity resourceEntity = resourceMapper.selectOne(Wrappers.<ResourceEntity>lambdaQuery().eq(ResourceEntity::getResCode, resCode));
                if (resourceEntity != null) {
                    throw BusinessExceptionBuilder.build(UserCode.RESOURCE_EXIST);
                }
                break;
            case UPDATE:
                if (StringUtils.isBlank(entity.getParentCode())) {
                    entity.setParentFlag(Enabled.YES.getValue());
                }
                break;
        }
    }

    @Override
    public void processAfterOperation(ResourceEntity entity, BaseOperationEnum operationEnum) throws BusinessException {
        switch (operationEnum) {
            case DELETE:
                String resCode = entity.getResCode();
                roleResourceMapper.delete(Wrappers.<RoleResourceEntity>lambdaQuery().eq(RoleResourceEntity::getResCode, resCode));
                break;
        }
    }

    @Override
    public List<String> getAllMenuUrls(List<String> roles) {
        if (CollectionUtils.isEmpty(roles)) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<RoleResourceEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoleResourceEntity::getIsMenu, Enabled.YES.getValue());
        wrapper.in(RoleResourceEntity::getRoleCode, roles);
        List<String> resCodes = roleResourceMapper.selectList(wrapper)
                .stream()
                .map(RoleResourceEntity::getResCode)
                .collect(Collectors.toList());

        LambdaQueryWrapper<ResourceEntity> resourceWrapper = Wrappers.lambdaQuery();
        resourceWrapper.in(ResourceEntity::getResCode, resCodes);

        return resourceMapper.selectList(resourceWrapper)
                .stream()
                .map(ResourceEntity::getResCode)
                .collect(Collectors.toList());
    }
}
